﻿using Mozi.Live.RTP;
using System;
using System.Collections.Generic;

namespace Mozi.Live
{
    //TODO 媒体层承载媒体缓存能力
    //TODO 缓存功能暂时还有问题，暂不用
    /// <summary>
    /// 媒体层
    /// </summary>
    public class MediaCache
    {
        private List<MediaResource> _res = new List<MediaResource>();

        public MediaResource Find(string resourceName)
        {
            return _res.Find(x => resourceName==x.ResourceName);
        }

        public MediaResource First()
        {
            return _res.Count > 0 ? _res[0] : null;
        }

        public void Add(MediaResource buffer)
        {
            _res.Add(buffer);
        }

        public MediaResource Query(string resourceName)
        {
            return _res.Find(x => resourceName.StartsWith(x.ResourceName));
        }

        //public MediaResource FindBySSRC(string ssrcName)
        //{
        //    return _buffers.Find(x=>x.i)
        //}
    }
    //TODO 媒体缓冲缓冲的数据量

    /// <summary>
    /// 媒体资源缓存
    /// </summary>
    public class MediaResource
    {
        /// <summary>
        /// 流标识符
        /// </summary>
        public string ResourceName { get; set; }
        /// <summary>
        /// SDP描述文件
        /// </summary>
        public SDP.SDPPackage Description { get; set; }
        /// <summary>
        /// 缓存数据
        /// </summary>
        public List<MediaBuffer> Buffers { get; set; }
        /// <summary>
        /// 信源数量
        /// </summary>
        public int SourceCount { get { return Buffers.Count; } }
        /// <summary>
        /// 资源创建的时间
        /// </summary>
        public DateTime CreateTime { get; set; }
        /// <summary>
        /// 实例化
        /// </summary>
        public MediaResource()
        {
            Buffers = new List<MediaBuffer>();
            CreateTime = DateTime.Now;
        }
        /// <summary>
        /// 设置描述文件
        /// </summary>
        /// <param name="pack"></param>
        public void SetDescription( SDP.SDPPackage pack)
        {
            Description = pack;
        }
        public void AddBuffer(MediaBuffer buffer)
        {
            Buffers.Add(buffer);
        }
        /// <summary>
        /// 媒体信息
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="pack"></param>
        public void AddMedia(int channel,RTPPackage pack)
        {
            var buffer = Buffers.Find(x => x.TransportChannel == channel);
            if (buffer != null)
            {
                buffer.Push(pack);
            }
        }
        public void PopAll()
        {
            Buffers.ForEach(x =>
            {
                x.Pop();
            });
        }
        /// <summary>
        /// 清除所有缓存区数据
        /// </summary>
        public void ClearAll()
        {
            Buffers.ForEach(x =>
            {
                x.Clear();
            });
        }
    }

    public class MediaBuffer
    {
        private RTPBuffer buffer;

        /// <summary>
        /// 流编号
        /// </summary>
        public string StreamId { get; set; }
        /// <summary>
        /// 媒体类型值
        /// </summary>
        public int PayloadType { get; set; }
        /// <summary>
        /// 编码方式
        /// </summary>
        public string EncodingName { get; set; }
        /// <summary>
        /// 时钟频率
        /// </summary>
        public int ClockRate { get; set; }
        /// <summary>
        /// 声道
        /// </summary>
        public int Channel { get; set; }
        /// <summary>
        /// 源编号
        /// </summary>
        public uint SourceIdentifier { get; set; }
        /// <summary>
        /// 通讯信道
        /// </summary>
        public int TransportChannel { get; set; }

        public RTPBuffer Buffer { get => buffer; set => buffer = value; }
        /// <summary>
        /// 
        /// </summary>
        public MediaBuffer()
        {
            buffer = new RTPBuffer();
        }
        /// <summary>
        /// 释放一级缓存
        /// </summary>
        public void Pop()
        {
            buffer.Pop();
        }
        /// <summary>
        /// 缓存入队
        /// </summary>
        /// <param name="pack"></param>
        public void Push(RTPPackage pack)
        {
            buffer.Push(pack);
        }
        /// <summary>
        /// 清空缓存
        /// </summary>
        /// <remarks>清理一级二级缓存</remarks>
        public void Clear()
        {
            buffer.Reset();
        }
    }

    public class RTPBuffer
    {
        private RTPPackage[] _pool;

        private int indWrite = -1;
        private bool flushed = true;
        private int bufferSize;
        private int poolSize;

        private RTPPackage[] _packages;

        private object lockobj = new object();

        public  RTPPackage[] Packages
        {
            get
            {
                return _packages;
            }
        }

        public bool IsEmpty
        {
            get
            {
                return _packages[0]==null;
            }
        }
        /// <summary>
        /// 缓存堆中是否还有数据
        /// </summary>
        public bool HasCache
        {
            get
            {
                return indWrite >=0;
            }
        }
        /// <summary>
        /// 一级别缓存大小
        /// </summary>
        public int BufferSize { get => bufferSize; set => bufferSize = value; }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="bufferSize">缓存大小，RTP包的数量</param>
        public RTPBuffer(int bufferSize)
        {
            this.bufferSize = bufferSize;
            this.poolSize = bufferSize * 2;
            Reset();
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        public RTPBuffer() : this(24)
        {

        }

        public void Pop()
        {
            lock (lockobj)
            {
                flushed = true;
                Array.Clear(_packages,0,_packages.Length);
                Snap();
            }
        }
        private int Snap()
        {
            lock (lockobj)
            {
                if (flushed&&indWrite >= bufferSize)
                {

                    Array.Copy(_pool, _packages, _packages.Length);
                    
                    int tempSize = _pool.Length - _packages.Length;
                    if (tempSize > 0)
                    {

                        RTPPackage[] temp = new RTPPackage[tempSize];
                        Array.Copy(_pool, _packages.Length, temp, 0, tempSize);
                        Array.Resize(ref _pool, indWrite>=tempSize?(tempSize+poolSize): tempSize);
                        Array.Clear(_pool, 0, _pool.Length);
                        Array.Copy(temp, _pool, tempSize);
                        Array.Clear(temp, 0, tempSize);

                        indWrite -= _packages.Length;

                    }
                    else
                    {
                        _pool = new RTPPackage[poolSize];
                        indWrite = -1;
                    }
                    flushed = false;
                }
            }
            return indWrite;
        }

        public void Push(RTPPackage pack)
        {
            //try
            //{
                 lock (lockobj)
                 {
                    if (indWrite >= _pool.Length-1)
                    {
                        if (flushed)
                        {
                            Snap();
                        }
                        else
                        {
                            //动态扩容
                            Array.Resize(ref _pool, _pool.Length <= poolSize ? poolSize : (_pool.Length == poolSize) ? (poolSize + _pool.Length) : ((int)Math.Ceiling((double)(_pool.Length + 1) / poolSize)) * poolSize);
                        }
                    }
                    else
                    {

                    }
                    indWrite++;
                    _pool[indWrite] = pack;
                }

            //}catch(Exception ex){
            //    int a = 1;
            //    //indWrite--;
            //}
        }
        /// <summary>
        /// 清理一级二级缓存
        /// </summary>
        public void Reset()
        {
            lock (lockobj)
            {
                _packages = new RTPPackage[bufferSize];
                _pool = new RTPPackage[poolSize];
                indWrite = -1;
            }
        }
    }
}
